/*!
 * Replicator - Enhanced Element Cloning and Data Population
 * 
 * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
 * Dual licensed under:
 * 	MIT: http://www.opensource.org/licenses/mit-license.php
 *	GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
 */
(function($){
	
	/**
	 * Replicators need to be an instanciated class to allow for multiple simultaneous instances.
	 * The makeClass function by John Resig [Provided under the MIT License]
	 */
	if (!window.makeClass)	// makeClass - By John Resig (MIT Licensed)
		window.makeClass = function(){ return function (args){ if ( this instanceof arguments.callee ) this.init.apply( this, args.callee ? args : arguments ); else return new arguments.callee( arguments ) }}
	
	/** Plugin Options **/
	
	$.replication = {
		replacements: {},
		engines: {}
	}
	
	/** jQuery Entry Point **/
	
	$.fn.replicator = function(group, rules, container){
		return new replicator(group, this, container, rules);
	}
	
	$.fn.replicatorContainer = function(group, template, rules){
		return new replicator(group, container, this, rules);
	}
	
	// Extended cloning functionality which also stores references to used elements
	$.fn.replicate = function(group, container){
		if (!this.hasClass('replicator-template')) return this;
		
		var self = this.clone(true).removeClass('replicator-template').addClass('replicator-clone'), store = {};
		$.each($.replication.replacements[group], function(key){ store[key] = !this.selector ? self : $(this.selector, self) })
		
		if (container) self.appendTo(container);
		
		return self.data('ref', store);
	}
	
	/** Internals **/
	
	var replicator = $.Replicator = makeClass();
	
	$.extend(replicator.prototype, {
		init: function(group, template, container, rules){
			// Add instances to the replication namespace
			$.replication.engines[group] = this;
			$.replication.replacements[group] = rules||{};
			
			// Setup this replicator
			this.group = group;
			this.template = $(template).addClass('replicator-template').removeAttr('id').remove();
			this.container = container ? $(container) : null;
			this.allframes = null;
			this.length = 0;
		},
		setReplications: function(n, callback){
			if (n <= this.length){
				if (callback) callback.call(this);
				return this;
			}
			
			return this.replicate( n - this.length, callback );
		},
		replicate: function(n, callback){
			for (var i = 0; i < n; ++i){
				var clone = this.template.replicate(this.group, this.container);
				
				// Append to the allframes jQuery object
				if (!this.allframes) this.allframes = clone;
				else this.allframes = this.allframes.add(clone);
			} 
			
			this.length = this.allframes.length;
				
			if (callback) callback.call(this);
			return this;
		},
		hide: function(){
			this.allframes.hide();
			return this;
		},
		each: function(callback){ this.allframes.each(callback); },
		get: function(n){
			if (n > (this.length-1)) throw 'REPLICATOR_INVALID_INDEX_'+n;
			return this.allframes.eq(n);
		},
		update: function(frame, replacements){
			frame = this.get(frame);

			var _ = frame.data('ref'), rules = $.replication.replacements[this.group];

			$.each(rules, function(key){
				var el = _[key], param = this.apply ? null : [{}], val = replacements[this.value];

				if (this.type) param[0][this.type] = val;
				else if (!this.apply) param = [val]
				else param = val;

				el[ this.func ].apply(el, param);
			})
			
			return frame.show();
		}
	})
	
})(jQuery);
